package com.project.bookstore.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.project.bookstore.cache.repo.ILoginCacheRepository;
import com.project.bookstore.cache.repo.IPhoneVerificationCodeCacheRepository;
import com.project.bookstore.common.CommonResult;
import com.project.bookstore.common.utils.GlobalPasswordEncoder;
import com.project.bookstore.common.utils.NetworkUtil;
import com.project.bookstore.exception.ServiceInternalException;
import com.project.bookstore.exception.ServiceException;

import com.project.bookstore.user.domain.Bill;
import com.project.bookstore.user.mapper.AddressMapper;
import com.project.bookstore.user.mapper.BillMapper;
import com.project.bookstore.user.mapper.UserMapper;
import com.project.bookstore.user.pojo.*;
import com.project.bookstore.user.service.UserService;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import static com.project.bookstore.common.State.*;


@Service
@Transactional
@Slf4j
public class UserServiceImpl implements UserService {
    @Autowired
    private AddressMapper addressMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private BillMapper billMapper;

    @Autowired
    private GlobalPasswordEncoder globalPasswordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private ILoginCacheRepository loginCacheRepository;

    @Autowired
    private IPhoneVerificationCodeCacheRepository verificationCodeCacheRepository;

    @Value("${mine.JWT.secreteKey}")
    private String secreteKey;

    @Value("${mine.JWT.expiration}")
    private Long expiration;

    @Value("${mine.resources.location.avatar}")
    private String avatarLocation;

    @Override
    public String updateAvatar(MultipartFile userImg) throws IOException {
        String originalFilename = userImg.getOriginalFilename();
        log.debug("用户上传头像原文件名:【{}】", originalFilename);
        assert originalFilename != null;
        String sub = originalFilename.substring(originalFilename.lastIndexOf('.'));
        String fileUrl = UUID.randomUUID() + sub;
        //获取项目 static 文件夹的路径
        String path = ResourceUtils.getURL("classpath:").getPath() + avatarLocation;
        log.debug("文件保存的路径path【{}】", path);
        File fileDir = new File(path);
        if (!fileDir.exists())
            fileDir.mkdirs();
        userImg.transferTo(new File(fileDir + "/" + fileUrl));
        log.debug("文件保存的url:【{}】", fileUrl);
        return "/" + fileUrl;
    }

    @Override
    public CommonResult<Void> updatePersonalData(UserPersonalDataVO user) {
        if (userMapper.updatePersonalData(user) != 1) {
            log.warn("未知原因修改用户 (id:【{}】)信息失败", user.getId());
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<Void> updatePassword(Long id, String password, String oldPass) {
        String passwordData = userMapper.selectPasswordById(id);
        if (!passwordData.equals(oldPass)) {
            log.warn("密码错误");
            throw new ServiceException(FORBIDDEN, "原密码错误");
        }
        if (userMapper.updatePassword(id, password) != 1) {
            log.warn("修改密码时发生未知错误");
            throw new ServiceException(INTERNAL_EXCEPTION, "服务器忙，请稍后重试");
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<UserLoginVO> login(LoginVO user, HttpServletRequest request) {
        //创建需要认证的数据
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        log.debug("认证数据 authenticationToken:【{}】", authenticationToken);
        //认证失败会直接抛出异常
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        User principal = (User) authenticate.getPrincipal();
        log.debug("认证成功后authenticate.getPrincipal():【{}】", principal);
        //设置 jwt
        HashMap<String, Object> data = new HashMap<>();
        data.put("username", principal.getUsername());
        data.put("password", principal.getPassword());
        //这里获取到权限后为了格式化 我们用fastJson转为json格式
        data.put("permissions", JSON.toJSONString(principal.getAuthorities()));
        String longinJwtStr = Jwts.builder().setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setHeaderParam(Header.CONTENT_TYPE, "HS256")
                .setClaims(data)
//                .setExpiration(new Date(System.currentTimeMillis() + expiration * 60 * 1000))
                .signWith(SignatureAlgorithm.HS256, secreteKey)
                .compact();
        log.debug("认证成功后生成的 jwt 字符串:【{}】", longinJwtStr);
        Long id = userMapper.selectIdByUsername(user.getUsername());
        loginCacheRepository.setLoginToken(id, longinJwtStr);
        //获取用户登录IP
        String userIp = null;
        try {
            userIp = NetworkUtil.getIpAddress(request);
        } catch (IOException e) {
            log.warn("登录时获取用户IP地址出现 IO异常");
            e.printStackTrace();
        }
        //更新用户最近登录时间、ip地址
        UserLoginDO userLoginDO = new UserLoginDO();
        userLoginDO.setLastLogin(LocalDateTime.now())
                .setLastLoginIp(userIp)
                .setUsername(user.getUsername());
        log.debug("准备更新用户登录相关数据【{}】", userLoginDO);
        if (userMapper.updateLoginByUsername(userLoginDO) != 1) {
            log.warn("未知原因用户表登录相关更新失败");
            throw new ServiceException(INTERNAL_EXCEPTION, "服务器忙，请稍后尝试");
        }
        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setId(id).setLonginJwtStr(longinJwtStr);
        log.debug("登录业务完成，返回结果：【{}】",userLoginVO);
        return CommonResult.ok(userLoginVO);
    }

    @Override
    public CommonResult<MineInfoVO> mineInfo(Long id) {
        MineInfoVO mineInfoVO = userMapper.selectMineInfo(id);
        log.debug("根据用户ID查找数据库中个人中心相关数据:【{}】", mineInfoVO);
        if (Objects.isNull(mineInfoVO)) {
            log.warn("个人中心我的资料未找到用户信息 userId:【{}】", id);
            throw new ServiceException(NOT_FOUND, "未找到该用户信息");
        }
        return CommonResult.ok(mineInfoVO);
    }

    @Override
    public CommonResult<UserPersonalDataVO> getPersonalData(Long id) {
        UserPersonalDataVO userPersonalDataVO = userMapper.getPersonalData(id);
        if (userPersonalDataVO == null) {
            log.warn("根据ID:【{}】查找用户个人信息为空", id);
            throw new ServiceException(NOT_FOUND, "未找到该用户信息");
        }
        return CommonResult.ok(userPersonalDataVO);
    }

    @Override
    public CommonResult<Void> getState(Long id, HttpServletRequest request) {
        if (!loginCacheRepository.hasLoginToken(id)) {
            log.debug("缓存中不存在用户ID【{}】的登录 token", id);
            throw new ServiceException(NOT_FOUND, "用户未登录");
        }
        String authentication = request.getHeader("Authentication");
        log.debug("验证登录状态，请求头中 Authentication:【{}】", authentication);
        String loginToken = loginCacheRepository.getLoginToken(id);
        if (!loginToken.equals(authentication)) {
            log.warn("该用户缓存中的不匹配 Authentication【{}】", loginToken);
            throw new ServiceException(BAD_REQUEST, "登录异常请重新登录");
        }
        log.debug("与缓存中 token 匹配,重置缓存中 token 时间");
        //重置 token 时间
        loginCacheRepository.resetLoginTokenExpire(id);
        return CommonResult.ok();
    }

    @Override
    public CommonResult register(RegisterVO registerVO) {
        Long userId;
        if ((userId = userMapper.selectIdByUsername(registerVO.getUsername())) != null) {
            log.warn("用户注册失败,用户名【{}】已存在,用户ID【{}】", registerVO.getUsername(), userId);
            throw new ServiceException(CONFLICT, "注册失败，该用户名已存在");
        }
        com.project.bookstore.user.domain.User user = new com.project.bookstore.user.domain.User();
        BeanUtils.copyProperties(registerVO, user);
        log.debug("对密码【{}】进行加密", registerVO.getPassword());
        user.setAvatar(avatarLocation + "/defaultAvatar.png")
                .setPassword(globalPasswordEncoder.encode(registerVO.getPassword()))
                .setNickChange(2)
                .setGender("2")
                .setLoginCount(0)
                .setCreated(LocalDateTime.now())
                .setModified(LocalDateTime.now());
        log.debug("准备注册,默认数据为:【{}】", user);
        if (userMapper.myInsert(user) != 1) {
            log.warn("未知原因注册失败,客户端数据:【{}】", registerVO);
            throw new ServiceException(INTERNAL_EXCEPTION, "服务器忙，请稍后重试");
        }
        log.debug("用户注册成功,准备自动开通初始账户");
        Bill bill = new Bill();
        bill.setAccount(UUID.randomUUID().toString())
                .setBalance(BigDecimal.ZERO)
                .setRemarks("初始资金账号")
                .setCreated(LocalDateTime.now())
                .setUserId(user.getId());
        log.debug("默认初始账户数据:【{}】", bill);
        if (billMapper.insert(bill) != 1) {
            log.warn("位置原因开通资金账户失败");
            throw new ServiceException(INTERNAL_EXCEPTION, "服务器忙请重试");
        }
        log.debug("资金账户注册成功，注册完成");
        return CommonResult.ok();
    }

    @Override
    public CommonResult checkIsUserNameExist(String userName) {
        if (userMapper.selectIdByUsername(userName) != null) {
            log.warn("用户名【{}】已存在", userName);
            throw new ServiceException(CONFLICT, "用户名已存在");
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<Void> checkVerificationCode(String phone, String verificationCode) {
        String code = verificationCodeCacheRepository.getCode(phone);
        if (code == null) {
            log.debug("手机号【{}】的验证码不存在", phone);
            throw new ServiceException(BAD_REQUEST, "验证码已失效");
        }
        if (!code.equals(verificationCode)) {
            log.debug("用户输入【{}】与缓存验证码【{}】不匹配", verificationCode, code);
            throw new ServiceException(ERROR_PASSWORD, "验证码错误");
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<Void> checkPassword(Long id, String password) {
        String passwordDB = userMapper.selectPasswordById(id);
        log.debug("用户ID【{}】验证密码时，数据库密码为【{}】", id, passwordDB);
        if (passwordDB == null) {
            log.warn("用户ID：【{}】的用户密码为空", id);
            throw new ServiceException(INTERNAL_EXCEPTION, "服务器忙请稍后尝试");
        }
        if (!globalPasswordEncoder.match(password, passwordDB)) {
            log.warn("验证密码错误用户ID：【{}】输入密码：【{}】", id, password);
            throw new ServiceException(FORBIDDEN, "密码错误");
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<String> getPhoneById(Long id) {
        String phone = "";
        if ((phone = userMapper.selectPhoneById(id)) == null) {
            log.warn("未知原因【{}】的手机号为空", id);
            throw new ServiceInternalException();
        }
        return CommonResult.ok(phone);
    }

    @Override
    public CommonResult<Void> logout(Long id) {
        log.debug("id为【{}】的用户 登出", id);
        loginCacheRepository.removeLoginToken(id);
        return CommonResult.ok();
    }

    @Override
    public CommonResult updateNick(Long userId,String nick) {
        if (userMapper.selectNickChange(userId)<1){
            throw  new ServiceException(BAD_REQUEST,"修改次数不足");
        }
        //处理提交昵称会自动在后面加一个 = 的问题
        if (nick.endsWith("="))
            nick = nick.substring(0,nick.lastIndexOf("="));
        if (userMapper.updateNickById(userId,nick)!=1){
            log.warn("修改昵称时发生未知错误");
            throw new ServiceInternalException();
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<List<AddressForPayVO>> getAddressForPay(Long userId) {
        List<AddressForPayVO> address = addressMapper.listByUserId(userId);
        if (address==null){
            log.warn("根据用户ID获取地址信息时发生未知错误");
            throw new ServiceInternalException();
        }
        log.debug("获取到用户【{}】的地址信息为：【{}】,返回给前端",userId,address);
        return CommonResult.ok(address);
    }

    @Override
    public CommonResult<UserMenuPersonalSourceVO> getMenuPersonalSource(Long id) {
        UserMenuPersonalSourceVO menuPersonalSourceVO = userMapper.selectNickAvatarById(id);
        if (menuPersonalSourceVO == null) {
            log.warn("未知原因获取首部菜单栏个人资源失败,id:【{}】", id);
            throw new ServiceInternalException();
        }
        return CommonResult.ok(menuPersonalSourceVO);
    }

    @Override
    public CommonResult<String> getEmailById(Long id) {
        String e = "";
        if ((e = userMapper.selectEmailById(id)) == null) {
            log.warn("未知原因【{}】的邮箱为空", id);
            throw new ServiceInternalException();
        }
        return CommonResult.ok(e);
    }

    @Override
    public CommonResult updateEmail(Long id, String email) {
        //对数据进行处理 例 1260330852%40.qq.com=
        email = email.replace("%40", "@");
        if (email.contains("="))
            email = email.replace("=", "");
        log.debug("准备修改用户邮箱，新邮箱:【{}】", email);
        if (userMapper.updateEmailById(id, email) != 1) {
            throw new ServiceInternalException();
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult updatePhone(Long id, String phone) {
        if (phone.contains("="))
            phone = phone.replace("=", "");
        log.debug("准备更细用户手机号：【{}】", phone);
        if (userMapper.updatePhoneById(id, phone) != 1) {
            throw new ServiceInternalException();
        }
        return CommonResult.ok();
    }

    @Override
    public CommonResult<String> getVerificationCode(String phone) {
        StringBuilder verificationCodeBuilder = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            verificationCodeBuilder.append((int) (Math.random() * 10));
        }
        String verificationCode = verificationCodeBuilder.toString();
        log.debug("通过random获得6位数字验证码为【{}】", verificationCode);
        verificationCodeCacheRepository.setCode(phone, verificationCode);
        return CommonResult.ok(verificationCode);
    }
}